/*
 * FrameMain.java
 *
 * Created on 07 October 2008, 19:02
 */

package ui;

import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyVetoException;
import java.util.Enumeration;
import java.util.LinkedList;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JInternalFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeModel;
import ui.error.Error;
import ui.error.ErrorListener;
import netlist.Netlist;
import sim.SimulatorState;
import ui.clipboard.Clipboard;
import ui.error.ErrorHandler;
import ui.components.VisualComponent;
import ui.components.SelectableComponent;

/**
 *
 * @author  Matt
 */
public class Editor extends javax.swing.JFrame implements ErrorListener {
    private CircuitPanel circuitPanel;
    private LinkedList<Netlist> netlists = new LinkedList<Netlist>();
    private Image offscreenImage;
    private Graphics offscreenGraphics;
    private boolean drawDirect = false;
    private int untitledIndex = 1;    
    private Clipboard clipboard = new Clipboard();
    private LinkedList<JInternalFrame> circuitwindows = new LinkedList<JInternalFrame>();
    JDialog aboutBox = new AboutBox(this);
    private SelectableComponent sc = null;
    private String titleNew, titleOld;   
    private double rotation = 0;
    private EventHandler controller;
        
    public Editor() {    
        initComponents();
        controller = new EventHandler(this);
        ResourceBundle bundle = java.util.ResourceBundle.getBundle("ui/Bundle"); // NOI18N
        setIconImage(new javax.swing.ImageIcon(this.getClass().getResource("/ui/images/buttons/toolbar/led.png")).getImage());      
        this.addWindowListener(new WindowAdapter(){
            public void windowClosing(WindowEvent e) {
                controller.ExitActionPerformed(null);
            }
        });         
        optionsPanel.setVisible(false);
        
        titleNew = bundle.getString("OptionsPanel.titleNew.text"); // NOI18N
        titleOld = bundle.getString("OptionsPanel.titleOld.text"); // NOI18N     
       
        clipboard.addSelectionListener(Cut);
        clipboard.addSelectionListener(Copy);
        clipboard.addSelectionListener(Delete);
        clipboard.addSelectionListener(CutButton);
        clipboard.addSelectionListener(CopyButton);
        clipboard.addSelectionListener(DeleteSelection);
        clipboard.addPasteListener(Paste);
        clipboard.addPasteListener(PasteButton);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {
        java.awt.GridBagConstraints gridBagConstraints;

        OptionsPane = new javax.swing.JScrollPane();
        Toolbar = new javax.swing.JToolBar();
        NewButton = new javax.swing.JButton();
        OpenFileButton = new javax.swing.JButton();
        SaveButton = new javax.swing.JButton();
        SaveAsButton = new javax.swing.JButton();
        jSeparator3 = new javax.swing.JToolBar.Separator();
        CutButton = new javax.swing.JButton();
        CopyButton = new javax.swing.JButton();
        PasteButton = new javax.swing.JButton();
        DeleteSelection = new javax.swing.JButton();
        ClearCircuit = new javax.swing.JButton();
        UndoButton = new javax.swing.JButton();
        RedoButton = new javax.swing.JButton();
        jSeparator6 = new javax.swing.JToolBar.Separator();
        MakeImageButton = new javax.swing.JButton();
        ToggleGrid = new javax.swing.JButton();
        jSeparator5 = new javax.swing.JToolBar.Separator();
        RecordButton = new javax.swing.JButton();
        StartButton = new javax.swing.JButton();
        StopButton = new javax.swing.JButton();
        StepForwardButton = new javax.swing.JButton();
        SimulatorSpeed = new javax.swing.JSlider();
        Toolbox = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        ToolboxButtons = new javax.swing.JPanel();
        Selection = new javax.swing.JButton();
        Wire = new javax.swing.JButton();
        InsertSubComponent = new javax.swing.JButton();
        RotateLeft = new javax.swing.JButton();
        RotateRight = new javax.swing.JButton();
        SelectionTreeScrollPane = new javax.swing.JScrollPane();
        ComponentSelectionTree = new javax.swing.JTree();
        optionsPanel = new JPanel();
        titleLabel = new javax.swing.JLabel();
        typeLabel = new javax.swing.JLabel();
        AttributesPanel = new javax.swing.JPanel();
        Preview = new PreviewPanel(this);
        MainScrollPane = new javax.swing.JScrollPane();
        DesktopPane = new ScrollableDesktop();
        statusPanel = new javax.swing.JPanel();
        javax.swing.JSeparator statusPanelSeparator = new javax.swing.JSeparator();
        statusMessageLabel = new javax.swing.JLabel();
        statusAnimationLabel = new javax.swing.JLabel();
        progressBar = new javax.swing.JProgressBar();
        MenuBar = new javax.swing.JMenuBar();
        File = new javax.swing.JMenu();
        New = new javax.swing.JMenuItem();
        Open = new javax.swing.JMenuItem();
        Save = new javax.swing.JMenuItem();
        SaveAs = new javax.swing.JMenuItem();
        Preferences = new javax.swing.JMenuItem();
        Exit = new javax.swing.JMenuItem();
        Edit = new javax.swing.JMenu();
        Undo = new javax.swing.JMenuItem();
        Redo = new javax.swing.JMenuItem();
        jSeparator1 = new javax.swing.JSeparator();
        Cut = new javax.swing.JMenuItem();
        Copy = new javax.swing.JMenuItem();
        Paste = new javax.swing.JMenuItem();
        SelectAll = new javax.swing.JMenuItem();
        jSeparator2 = new javax.swing.JSeparator();
        Delete = new javax.swing.JMenuItem();
        Simulation = new javax.swing.JMenu();
        Run = new javax.swing.JMenuItem();
        Stop = new javax.swing.JMenuItem();
        StepForward = new javax.swing.JMenuItem();
        Record = new javax.swing.JMenuItem();
        Window = new javax.swing.JMenu();
        Help = new javax.swing.JMenu();
        About = new javax.swing.JMenuItem();

        OptionsPane.setBorder(null);

        setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
        java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("ui/Bundle"); // NOI18N
        setTitle(bundle.getString("Editor.title")); // NOI18N
        setBounds(new java.awt.Rectangle(0, 0, 985, 750));
        setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
        setMinimumSize(new java.awt.Dimension(750, 750));

        Toolbar.setFloatable(false);
        Toolbar.setRollover(true);
        Toolbar.setBorderPainted(false);
        Toolbar.setMaximumSize(new java.awt.Dimension(2000, 34));
        Toolbar.setMinimumSize(new java.awt.Dimension(750, 34));
        Toolbar.setPreferredSize(new java.awt.Dimension(750, 34));

        NewButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/document-new.png"))); // NOI18N
        NewButton.setText(bundle.getString("Editor.NewButton.text")); // NOI18N
        NewButton.setFocusable(false);
        NewButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        NewButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        NewButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.NewButtonMouseClicked(evt);
            }
        });
        Toolbar.add(NewButton);

        OpenFileButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/document-open.png"))); // NOI18N
        OpenFileButton.setText(bundle.getString("Editor.OpenFileButton.text")); // NOI18N
        OpenFileButton.setFocusable(false);
        OpenFileButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        OpenFileButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        OpenFileButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.OpenFileButtonMouseClicked(evt);
            }
        });
        Toolbar.add(OpenFileButton);

        SaveButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/document-save.png"))); // NOI18N
        SaveButton.setText(bundle.getString("Editor.SaveButton.text")); // NOI18N
        SaveButton.setEnabled(false);
        SaveButton.setFocusable(false);
        SaveButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        SaveButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        SaveButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.SaveButtonMouseClicked(evt);
            }
        });
        Toolbar.add(SaveButton);

        SaveAsButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/document-save-as.png"))); // NOI18N
        SaveAsButton.setText(bundle.getString("Editor.SaveAsButton.text")); // NOI18N
        SaveAsButton.setEnabled(false);
        SaveAsButton.setFocusable(false);
        SaveAsButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        SaveAsButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        SaveAsButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.SaveAsButtonMouseClicked(evt);
            }
        });
        Toolbar.add(SaveAsButton);
        Toolbar.add(jSeparator3);

        CutButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-cut.png"))); // NOI18N
        CutButton.setText(bundle.getString("Editor.CutButton.text")); // NOI18N
        CutButton.setFocusable(false);
        CutButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        CutButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        CutButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.CutButtonMouseClicked(evt);
            }
        });
        Toolbar.add(CutButton);

        CopyButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-copy.png"))); // NOI18N
        CopyButton.setText(bundle.getString("Editor.CopyButton.text")); // NOI18N
        CopyButton.setFocusable(false);
        CopyButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        CopyButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        CopyButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.CopyButtonMouseClicked(evt);
            }
        });
        Toolbar.add(CopyButton);

        PasteButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-paste.png"))); // NOI18N
        PasteButton.setText(bundle.getString("Editor.PasteButton.text")); // NOI18N
        PasteButton.setEnabled(false);
        PasteButton.setFocusable(false);
        PasteButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        PasteButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        PasteButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.PasteButtonMouseClicked(evt);
            }
        });
        Toolbar.add(PasteButton);

        DeleteSelection.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-delete.png"))); // NOI18N
        DeleteSelection.setText(bundle.getString("Editor.DeleteSelection.text")); // NOI18N
        DeleteSelection.setToolTipText(bundle.getString("Editor.DeleteSelection.toolTipText")); // NOI18N
        DeleteSelection.setFocusable(false);
        DeleteSelection.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        DeleteSelection.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        DeleteSelection.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.DeleteSelectionMouseClicked(evt);
            }
        });
        Toolbar.add(DeleteSelection);

        ClearCircuit.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-clear.png"))); // NOI18N
        ClearCircuit.setText(bundle.getString("Editor.ClearCircuit.text")); // NOI18N
        ClearCircuit.setToolTipText(bundle.getString("Editor.ClearCircuit.toolTipText")); // NOI18N
        ClearCircuit.setFocusable(false);
        ClearCircuit.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        ClearCircuit.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        ClearCircuit.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.ClearCircuitMouseClicked(evt);
            }
        });
        Toolbar.add(ClearCircuit);

        UndoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-undo.png"))); // NOI18N
        UndoButton.setText(bundle.getString("Editor.UndoButton.text")); // NOI18N
        UndoButton.setEnabled(false);
        UndoButton.setFocusable(false);
        UndoButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        UndoButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        UndoButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.UndoButtonMouseClicked(evt);
            }
        });
        Toolbar.add(UndoButton);

        RedoButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/edit-redo.png"))); // NOI18N
        RedoButton.setText(bundle.getString("Editor.RedoButton.text")); // NOI18N
        RedoButton.setEnabled(false);
        RedoButton.setFocusable(false);
        RedoButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        RedoButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        RedoButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.RedoButtonMouseClicked(evt);
            }
        });
        Toolbar.add(RedoButton);
        Toolbar.add(jSeparator6);

        MakeImageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/create-image.png"))); // NOI18N
        MakeImageButton.setText(bundle.getString("Editor.MakeImageButton.text")); // NOI18N
        MakeImageButton.setFocusable(false);
        MakeImageButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        MakeImageButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        MakeImageButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.MakeImageButtonMouseClicked(evt);
            }
        });
        Toolbar.add(MakeImageButton);

        ToggleGrid.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/grid.png"))); // NOI18N
        ToggleGrid.setText(bundle.getString("Editor.ToggleGrid.text")); // NOI18N
        ToggleGrid.setFocusable(false);
        ToggleGrid.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        ToggleGrid.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        ToggleGrid.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.ToggleGridActionPerformed(evt);
            }
        });
        Toolbar.add(ToggleGrid);
        Toolbar.add(jSeparator5);

        RecordButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-record.png"))); // NOI18N
        RecordButton.setText(bundle.getString("Editor.RecordButton.text")); // NOI18N
        RecordButton.setFocusable(false);
        RecordButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        RecordButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        RecordButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.RecordButtonMouseClicked(evt);
            }
        });
        Toolbar.add(RecordButton);

        StartButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-playback-start.png"))); // NOI18N
        StartButton.setText(bundle.getString("Editor.StartButton.text")); // NOI18N
        StartButton.setFocusable(false);
        StartButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        StartButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        StartButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.StartButtonMouseClicked(evt);
            }
        });
        Toolbar.add(StartButton);

        StopButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-playback-stop.png"))); // NOI18N
        StopButton.setText(bundle.getString("Editor.StopButton.text")); // NOI18N
        StopButton.setEnabled(false);
        StopButton.setFocusable(false);
        StopButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        StopButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        StopButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.StopButtonMouseClicked(evt);
            }
        });
        Toolbar.add(StopButton);

        StepForwardButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-seek-forward.png"))); // NOI18N
        StepForwardButton.setText(bundle.getString("Editor.StepForwardButton.text")); // NOI18N
        StepForwardButton.setEnabled(false);
        StepForwardButton.setFocusable(false);
        StepForwardButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        StepForwardButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        StepForwardButton.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.StepForwardButtonMouseClicked(evt);
            }
        });
        Toolbar.add(StepForwardButton);

        SimulatorSpeed.setMajorTickSpacing(1);
        SimulatorSpeed.setMaximum(10);
        SimulatorSpeed.setPaintTicks(true);
        SimulatorSpeed.setSnapToTicks(true);
        SimulatorSpeed.setMaximumSize(new java.awt.Dimension(200, 33));
        SimulatorSpeed.addChangeListener(new javax.swing.event.ChangeListener() {
            public void stateChanged(javax.swing.event.ChangeEvent evt) {
                controller.SimulatorSpeedStateChanged(evt);
            }
        });
        Toolbar.add(SimulatorSpeed);

        getContentPane().add(Toolbar, java.awt.BorderLayout.NORTH);

        Toolbox.setLayout(new org.netbeans.lib.awtextra.AbsoluteLayout());

        jLabel1.setForeground(javax.swing.UIManager.getDefaults().getColor("Button.darkShadow"));
        jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
        jLabel1.setText(bundle.getString("Editor.jLabel1.text")); // NOI18N
        Toolbox.add(jLabel1, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 0, 170, -1));

        ToolboxButtons.setLayout(new java.awt.GridBagLayout());

        Selection.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/sml_select.png"))); // NOI18N
        Selection.setText(bundle.getString("TestJFrameForm.Selection.text")); // NOI18N
        Selection.setMargin(new java.awt.Insets(2, 2, 2, 2));
        Selection.setMaximumSize(new java.awt.Dimension(32, 32));
        Selection.setMinimumSize(new java.awt.Dimension(32, 32));
        Selection.setPreferredSize(new java.awt.Dimension(32, 32));
        Selection.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.SelectionToolClicked(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        ToolboxButtons.add(Selection, gridBagConstraints);

        Wire.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/sml_wire.png"))); // NOI18N
        Wire.setText(bundle.getString("TestJFrameForm.Wire.text")); // NOI18N
        Wire.setToolTipText(bundle.getString("TestJFrameForm.Wire.toolTipText")); // NOI18N
        Wire.setMargin(new java.awt.Insets(2, 2, 2, 2));
        Wire.setMaximumSize(new java.awt.Dimension(32, 32));
        Wire.setMinimumSize(new java.awt.Dimension(32, 32));
        Wire.setPreferredSize(new java.awt.Dimension(32, 32));
        Wire.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.WireMouseClicked(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        ToolboxButtons.add(Wire, gridBagConstraints);

        InsertSubComponent.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/insert-object.png"))); // NOI18N
        InsertSubComponent.setText(bundle.getString("Editor.InsertSubComponent.text")); // NOI18N
        InsertSubComponent.setFocusable(false);
        InsertSubComponent.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        InsertSubComponent.setMaximumSize(new java.awt.Dimension(32, 32));
        InsertSubComponent.setMinimumSize(new java.awt.Dimension(32, 32));
        InsertSubComponent.setPreferredSize(new java.awt.Dimension(32, 32));
        InsertSubComponent.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        InsertSubComponent.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.InsertSubComponentActionPerformed(evt);
            }
        });
        ToolboxButtons.add(InsertSubComponent, new java.awt.GridBagConstraints());

        RotateLeft.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/object-rotate-left.png"))); // NOI18N
        RotateLeft.setText(bundle.getString("Editor.RotateLeft.text")); // NOI18N
        RotateLeft.setFocusable(false);
        RotateLeft.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        RotateLeft.setMaximumSize(new java.awt.Dimension(32, 32));
        RotateLeft.setMinimumSize(new java.awt.Dimension(32, 32));
        RotateLeft.setPreferredSize(new java.awt.Dimension(32, 32));
        RotateLeft.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        RotateLeft.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.RotateLeftMouseClicked(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 3;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        ToolboxButtons.add(RotateLeft, gridBagConstraints);

        RotateRight.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/object-rotate-right.png"))); // NOI18N
        RotateRight.setText(bundle.getString("Editor.RotateRight.text")); // NOI18N
        RotateRight.setFocusable(false);
        RotateRight.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        RotateRight.setMaximumSize(new java.awt.Dimension(32, 32));
        RotateRight.setMinimumSize(new java.awt.Dimension(32, 32));
        RotateRight.setPreferredSize(new java.awt.Dimension(32, 32));
        RotateRight.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        RotateRight.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                controller.RotateRightMouseClicked(evt);
            }
        });
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 4;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        ToolboxButtons.add(RotateRight, gridBagConstraints);

        Toolbox.add(ToolboxButtons, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 20, 170, 30));

        SelectionTreeScrollPane.setAutoscrolls(true);

        ComponentSelectionTree.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                controller.ComponentSelectionTreeValueChanged(evt);
            }
        });
        ComponentSelectionTree.addFocusListener(new java.awt.event.FocusAdapter() {
            public void focusGained(java.awt.event.FocusEvent evt) {
                controller.ComponentSelectionTreeFocusGained(evt);
            }
        });
        ComponentSelectionTree.setModel(getTreeValues());
        ComponentSelectionTree.setRootVisible(false);
        SelectionTreeScrollPane.setViewportView(ComponentSelectionTree);

        Toolbox.add(SelectionTreeScrollPane, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 60, 170, 250));

        optionsPanel.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 2, 1, 2));
        optionsPanel.setMaximumSize(new java.awt.Dimension(72, 232));
        optionsPanel.setMinimumSize(new java.awt.Dimension(72, 232));
        optionsPanel.setPreferredSize(new java.awt.Dimension(72, 232));

        titleLabel.setText(titleNew);
        titleLabel.setForeground(new java.awt.Color(108, 108, 108));
        titleLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);

        typeLabel.setText(bundle.getString("OptionsPanel.jLabel2.text"));

        AttributesPanel.setMaximumSize(new java.awt.Dimension(72, 500));
        AttributesPanel.setMinimumSize(new java.awt.Dimension(72, 72));
        AttributesPanel.setPreferredSize(new java.awt.Dimension(72, 72));
        AttributesPanel.setLayout(new java.awt.BorderLayout());

        Preview.setMinimumSize(new java.awt.Dimension(72, 80));
        Preview.setPreferredSize(new java.awt.Dimension(72, 80));

        org.jdesktop.layout.GroupLayout optionsPanelLayout = new org.jdesktop.layout.GroupLayout(optionsPanel);
        optionsPanel.setLayout(optionsPanelLayout);
        optionsPanelLayout.setHorizontalGroup(
            optionsPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(Preview, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 166, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
            .add(titleLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 166, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
            .add(typeLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 166, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
            .add(AttributesPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 166, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
        );
        optionsPanelLayout.setVerticalGroup(
            optionsPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(optionsPanelLayout.createSequentialGroup()
                .add(titleLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(typeLabel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 14, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(AttributesPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 151, Short.MAX_VALUE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(Preview, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        Toolbox.add(optionsPanel, new org.netbeans.lib.awtextra.AbsoluteConstraints(0, 320, 170, 290));

        getContentPane().add(Toolbox, java.awt.BorderLayout.WEST);

        MainScrollPane.setPreferredSize(new java.awt.Dimension(800, 600));

        DesktopPane.setBackground(javax.swing.UIManager.getDefaults().getColor("Panel.background"));
        DesktopPane.setAutoscrolls(true);
        DesktopPane.setDoubleBuffered(true);
        DesktopPane.setMinimumSize(new java.awt.Dimension(600, 400));
        MainScrollPane.setViewportView(DesktopPane);

        getContentPane().add(MainScrollPane, java.awt.BorderLayout.CENTER);

        statusMessageLabel.setFont(new java.awt.Font("Lucida Sans", 0, 11));
        statusMessageLabel.setText(bundle.getString("Editor.statusMessageLabel.text")); // NOI18N

        statusAnimationLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);

        org.jdesktop.layout.GroupLayout statusPanelLayout = new org.jdesktop.layout.GroupLayout(statusPanel);
        statusPanel.setLayout(statusPanelLayout);
        statusPanelLayout.setHorizontalGroup(
            statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(statusPanelLayout.createSequentialGroup()
                .addContainerGap()
                .add(statusMessageLabel)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, 777, Short.MAX_VALUE)
                .add(progressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
                .add(statusAnimationLabel)
                .addContainerGap())
            .add(statusPanelSeparator, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 947, Short.MAX_VALUE)
        );
        statusPanelLayout.setVerticalGroup(
            statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
            .add(org.jdesktop.layout.GroupLayout.TRAILING, statusPanelLayout.createSequentialGroup()
                .add(statusPanelSeparator, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .add(statusPanelLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
                    .add(statusMessageLabel)
                    .add(statusAnimationLabel)
                    .add(progressBar, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
                .add(3, 3, 3))
        );

        getContentPane().add(statusPanel, java.awt.BorderLayout.PAGE_END);

        File.setMnemonic('F');
        File.setText(bundle.getString("TestJFrameForm.jMenu1.text_1")); // NOI18N

        New.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_N, java.awt.event.InputEvent.CTRL_MASK));
        New.setMnemonic('o');
        New.setText(bundle.getString("Editor.New.text")); // NOI18N
        New.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.NewActionPerformed(evt);
            }
        });
        File.add(New);

        Open.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK));
        Open.setMnemonic('o');
        Open.setText(bundle.getString("Editor.Open.text")); // NOI18N
        Open.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.OpenActionPerformed(evt);
            }
        });
        File.add(Open);

        Save.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK));
        Save.setMnemonic('s');
        Save.setText(bundle.getString("Editor.Save.text")); // NOI18N
        Save.setEnabled(false);
        Save.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.SaveActionPerformed(evt);
            }
        });
        File.add(Save);

        SaveAs.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.SHIFT_MASK | java.awt.event.InputEvent.CTRL_MASK));
        SaveAs.setMnemonic('v');
        SaveAs.setText(bundle.getString("Editor.SaveAs.text")); // NOI18N
        SaveAs.setEnabled(false);
        SaveAs.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.SaveAsActionPerformed(evt);
            }
        });
        File.add(SaveAs);

        Preferences.setText(bundle.getString("Editor.Preferences.text_1")); // NOI18N
        Preferences.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.PreferencesActionPerformed(evt);
            }
        });
        File.add(Preferences);

        Exit.setMnemonic('x');
        Exit.setText(bundle.getString("Editor.Exit.text")); // NOI18N
        Exit.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.ExitActionPerformed(evt);
            }
        });
        File.add(Exit);

        MenuBar.add(File);

        Edit.setMnemonic('E');
        Edit.setText(bundle.getString("Editor.Edit.text")); // NOI18N

        Undo.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK));
        Undo.setMnemonic('u');
        Undo.setText(bundle.getString("Editor.Undo.text")); // NOI18N
        Undo.setEnabled(false);
        Undo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.UndoActionPerformed(evt);
            }
        });
        Edit.add(Undo);

        Redo.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Y, java.awt.event.InputEvent.CTRL_MASK));
        Redo.setMnemonic('r');
        Redo.setText(bundle.getString("Editor.Redo.text")); // NOI18N
        Redo.setEnabled(false);
        Redo.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.RedoActionPerformed(evt);
            }
        });
        Edit.add(Redo);
        Edit.add(jSeparator1);

        Cut.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_MASK));
        Cut.setMnemonic('t');
        Cut.setText(bundle.getString("Editor.Cut.text")); // NOI18N
        Cut.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.CutActionPerformed(evt);
            }
        });
        Edit.add(Cut);

        Copy.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK));
        Copy.setMnemonic('y');
        Copy.setText(bundle.getString("Editor.Copy.text")); // NOI18N
        Copy.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.CopyActionPerformed(evt);
            }
        });
        Edit.add(Copy);

        Paste.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.event.InputEvent.CTRL_MASK));
        Paste.setMnemonic('p');
        Paste.setText(bundle.getString("Editor.Paste.text")); // NOI18N
        Paste.setEnabled(false);
        Paste.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.PasteActionPerformed(evt);
            }
        });
        Edit.add(Paste);

        SelectAll.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
        SelectAll.setMnemonic('a');
        SelectAll.setText(bundle.getString("Editor.SelectAll.text")); // NOI18N
        SelectAll.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.SelectAllActionPerformed(evt);
            }
        });
        Edit.add(SelectAll);
        Edit.add(jSeparator2);

        Delete.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_DELETE, 0));
        Delete.setMnemonic('d');
        Delete.setText(bundle.getString("Editor.Delete.text")); // NOI18N
        Delete.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.DeleteActionPerformed(evt);
            }
        });
        Edit.add(Delete);

        MenuBar.add(Edit);

        Simulation.setMnemonic('S');
        Simulation.setText(bundle.getString("Editor.Simulation.text")); // NOI18N

        Run.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_R, java.awt.event.InputEvent.CTRL_MASK));
        Run.setMnemonic('r');
        Run.setText(bundle.getString("Editor.Run.text_1")); // NOI18N
        Run.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.RunActionPerformed(evt);
            }
        });
        Simulation.add(Run);

        Stop.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_T, java.awt.event.InputEvent.CTRL_MASK));
        Stop.setMnemonic('t');
        Stop.setText(bundle.getString("Editor.Stop.text")); // NOI18N
        Stop.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.StopActionPerformed(evt);
            }
        });
        Simulation.add(Stop);

        StepForward.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F, java.awt.event.InputEvent.CTRL_MASK));
        StepForward.setMnemonic('F');
        StepForward.setText(bundle.getString("Editor.StepForward.text")); // NOI18N
        StepForward.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.StepForwardActionPerformed(evt);
            }
        });
        Simulation.add(StepForward);

        Record.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_L, java.awt.event.InputEvent.CTRL_MASK));
        Record.setMnemonic('l');
        Record.setText(bundle.getString("Editor.Record.text")); // NOI18N
        Record.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.RecordActionPerformed(evt);
            }
        });
        Simulation.add(Record);

        MenuBar.add(Simulation);

        Window.setMnemonic('W');
        Window.setText(bundle.getString("Editor.Window.text")); // NOI18N
        MenuBar.add(Window);

        Help.setMnemonic('H');
        Help.setText(bundle.getString("Editor.Help.text")); // NOI18N

        About.setText(bundle.getString("Editor.About.text")); // NOI18N
        About.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                controller.AboutActionPerformed(evt);
            }
        });
        Help.add(About);

        MenuBar.add(Help);

        setJMenuBar(MenuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents
  
    /** 
     * Repopulate the windows menu in the toolbar with the opened circuits
     */
    public void refreshWindowsMenu() {
        Window.removeAll();
        for(JInternalFrame cf: circuitwindows){
            final JInternalFrame cf2 = cf;
            javax.swing.JMenuItem windowItem = new javax.swing.JMenuItem();
            windowItem.setText(cf.getTitle()); 
            windowItem.addActionListener(new java.awt.event.ActionListener() {
                public void actionPerformed(java.awt.event.ActionEvent evt) {
                    try { cf2.setSelected(true);
                    } catch (PropertyVetoException ex) {
                        ErrorHandler.newError("Editor Error","Cannot select circuit " + cf2.getTitle() + ".", ex);
                    }
                }
            });
            Window.add(windowItem);            
        }
        if(circuitwindows.size()==0){
            Window.setEnabled(false);
        } else {
            Window.setEnabled(true);
        }
    }
    
    /** Reset selections of the logicalComponent toolbox so that we only have one 
     * button depressed at a time.
     * 
     * @param button    The button which is currently being selected
     */
    void toggleToolboxButton(JButton button){
        // Reset Selections
        Selection.setSelected(false);
        Wire.setSelected(false);
        RotateRight.setEnabled(true);
        RotateLeft.setEnabled(true);
        InsertSubComponent.setSelected(false);

        // Select this button
        button.setSelected(true);
        
        optionsPanel.setVisible(false);
        circuitPanel.removeUnfixedComponents();
    }

    /** Create a new empty circuit and open it in the Desktop Window Pane.
     * @return The new circuit */
    public CircuitPanel createBlankCircuit(boolean isSubCircuit) {
        
        // Construct the containing frame, also set position
        CircuitFrame cir = new CircuitFrame(this, untitledIndex++, isSubCircuit);
        if(getActiveCircuit() != null){
            cir.setBounds(getActiveCircuit().getParentFrame().getBounds().x,
                    getActiveCircuit().getParentFrame().getBounds().y,
                    cir.getWidth(),
                    cir.getHeight());
        }
        final CircuitFrame finalCir = cir;
         cir.addInternalFrameListener(new InternalFrameAdapter(){
            @Override
            public void internalFrameClosed(InternalFrameEvent e) {
                circuitwindows.remove(finalCir);
                if(circuitwindows.size()==0){
                    enableToolbar(false);
                    refreshWindowsMenu();
                }
            }
         });
        
        // Add the new frame to the list of windows and to the Desktop Window Pane
        circuitwindows.add(cir);
        enableToolbar(true);
        DesktopPane.add(cir, javax.swing.JDesktopPane.DEFAULT_LAYER);
        
        // Make the new circuit active
        setActiveCircuit(cir.getCircuitPanel());
        
        // Register the Command History components with the new circuits Command History
        cir.getCircuitPanel().getCommandHistory().addUndoEmptyListener(UndoButton);
        cir.getCircuitPanel().getCommandHistory().addRedoEmptyListener(RedoButton);
        cir.getCircuitPanel().getCommandHistory().addUndoEmptyListener(Undo);
        cir.getCircuitPanel().getCommandHistory().addRedoEmptyListener(Redo);
        
        // Populate Windows menu       
        refreshWindowsMenu();
        
        return getActiveCircuit();        
    }
    
    /** Enable/Disable the toolbar for when the first circuit is opened, or the 
     * last circuit is closed.*/
    private void enableToolbar(boolean enable) {
        Save.setEnabled(enable);
        SaveAs.setEnabled(enable);
        SaveButton.setEnabled(enable);
        SaveAsButton.setEnabled(enable);
        SelectAll.setEnabled(enable);
        ClearCircuit.setEnabled(enable);
        MakeImageButton.setEnabled(enable);
        ToggleGrid.setEnabled(enable);
        Simulation.setEnabled(enable);
        Edit.setEnabled(enable);
        StartButton.setEnabled(enable);
        RecordButton.setEnabled(enable);
        SimulatorSpeed.setEnabled(enable);
    }
    
    /** @return Tthe currently selected active circuit panel workarea. */
    public CircuitPanel getActiveCircuit() {
        return circuitPanel;
    }

    /** Make the specified circuit active */
    public void setActiveCircuit(CircuitPanel circuit) {
        if(circuitwindows.contains(circuit.getParentFrame())){
            // Set application's title
            setTitle("Logic Circuit Workbench - " + circuit.getParentFrame().getTitle());
            
            // Make the circuit active in the Desktop Window Pane
            this.circuitPanel = circuit;
            try {  circuit.getParentFrame().setSelected(true);  } 
            catch (PropertyVetoException ex) { 
                ErrorHandler.newError("Editor Error",
                        "Cannot select circuit " +
                        circuit.getParentFrame().getTitle() + ".", ex); 
            }

            // Set the appropriate state for all command history buttons
            UndoButton.setEnabled(circuitPanel.getCommandHistory().canUndo());
            RedoButton.setEnabled(circuitPanel.getCommandHistory().canRedo());
            Undo.setEnabled(circuitPanel.getCommandHistory().canUndo());
            Redo.setEnabled(circuitPanel.getCommandHistory().canRedo());
            
            clipboard.setHasSelection(circuitPanel.hasActiveSelection());
            
            // Set the appropriate state for all simulation buttons
            if(circuitPanel.getSimulatorState().equals(SimulatorState.STOPPED)){
                StartButton.setEnabled(true);
                StartButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-playback-start.png")));
                Run.setEnabled(true);
                StopButton.setEnabled(false);
                Stop.setEnabled(false);
                StepForwardButton.setEnabled(false);
                StepForward.setEnabled(false);                
            } else if(circuitPanel.getSimulatorState().equals(SimulatorState.PLAYING)){
                StartButton.setEnabled(true);
                StartButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-playback-pause.png")));
                Run.setEnabled(true);
                StopButton.setEnabled(true);
                Stop.setEnabled(true);
                StepForwardButton.setEnabled(false);
                StepForward.setEnabled(false);                
            } else if(circuitPanel.getSimulatorState().equals(SimulatorState.PAUSED)){
                StartButton.setEnabled(true);
                StartButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/ui/images/buttons/toolbar/media-playback-start.png")));
                Run.setEnabled(true);
                StopButton.setEnabled(true);
                Stop.setEnabled(true);
                StepForwardButton.setEnabled(true);
                StepForward.setEnabled(true);                
            }
            
            // Update Simulator speed slider
            SimulatorSpeed.setValue(circuitPanel.getSimulator().getSimulatorSpeed());
            
            // Is logger window open?
            RecordButton.setSelected(getActiveCircuit().getLoggerWindow().isShowing());
            Record.setSelected(getActiveCircuit().getLoggerWindow().isShowing());
        } else {
            ErrorHandler.newError("Editor Error",
                    "Error whilst setting an active circuit. \n " +
                    "Please close and reopen all open circuits.");
        }              
    }

    /** Construct the component tree from the list of netlists associated with this editor. 
     * @return rootNode     A well-formed TreeModel for use in a JTree */
    private TreeModel getTreeValues(){
        
        DefaultMutableTreeNode parent;
        DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("Components");
        String[] nodes = null;
        
        parent = rootNode;
        
        for(Netlist nl: netlists){
            for(String s: nl.keySet()){
                nodes = s.split("\\.");
                parent = rootNode;
                
                // Due to structure of keys, the tree is produced depth-first
                for(int i=0; i<nodes.length; i++){
                    // Create a new node for this sub category
                    DefaultMutableTreeNode mtn = new DefaultMutableTreeNode(nodes[i]);
                    
                    // Create the child nodes
                    Enumeration children = parent.children();
                    Boolean exists = false;
                    while(children.hasMoreElements()){
                        DefaultMutableTreeNode next = (DefaultMutableTreeNode) children.nextElement();
                        
                        // Check whether the next tree node already exists
                        if(next.getUserObject().equals(mtn.getUserObject())){ 
                            exists = true;
                            mtn = next;
                            break;
                        }
                    }
                    if(!exists){
                        parent.add(mtn);                    
                    }
                        
                    // We already have this node, move to the child
                    parent = mtn;            
                }                    
            }            
        }
        return new DefaultTreeModel(rootNode);
    }
    
    /** Unselect the currently selected component in the component selection tree. */
    public void clearComponentSelection(){
        ComponentSelectionTree.clearSelection();
    }
    
    /** Register a new Component Netlist with the editor. All components in the netlist 
     * will be made available in the toolbox for use in circuits.
     * 
     * @param nl The netlist to add. */
    public void addNetlist(Netlist nl){
        if(!nl.keySet().isEmpty()){
            netlists.add(nl);
            
            // Repopulate the Component tree
            ComponentSelectionTree.setModel(getTreeValues());
        }
    }
    
    /**  Checks whether a the key identifies either a valid implementation of a Selectable
     * Component which can be drawn on the circuit or a component which has a logical 
     * implementation but no visual representation. Components without a valid visual
     * (Selectable Component) representation are drawn dynamically from the properties of 
     * the logical component.
     * 
     * @param key
     * @return is key a valid netlist key? */
    public boolean isValidComponent(String key){
        //Remove "Components." from begining
        if(key.length() > 11 && key.subSequence(0, 11).equals("Components.")){
            key = key.substring(11); 
        }    
        
        // Check Logical Component mappings
        for(Netlist nl: netlists){
            if(nl.containsKey(key)){
                return true;
            }
        }    
        return false;
    }
    
    /** TODO Write javadoc
     * @param key
     * @return */
    public Netlist getNetlistWithKey(String key){
        //Remove "Components." from begining
        if(key.length() > 11 && key.subSequence(0, 11).equals("Components.")){
            key = key.substring(11); 
        }
        for(Netlist nl: netlists){
            if(nl.containsKey(key)){
                return nl;
            }
        }
        return null;
    }
    
    /** Fix a Component to the circuit
     * 
     * @param sc The component to fix */

        
    public Graphics getOffscreenGraphics() {
        return offscreenGraphics;
    }
    
    public Image getOffscreenImage() {
        return offscreenImage;
    }
    
    /** @return  the result of the fastest place to draw */
    public boolean drawDirect() {
        return drawDirect;
    }
        
    /** Tries drawing to the screen and to a buffer. Whichever one takes the least 
     * time (actually, whichever one it can do the most times within a set time constraint) is
     * the one that is best.
     * From: Expert Solutions by Mark Wutka, et. al. (http://www.webbasedprogramming.com/Java-Expert-Solutions/)*/
     public void doAutoDetect()
     {
        Graphics g = this.getGraphics();        
        // Create the off-screen drawing area
        offscreenImage = createImage(getWidth(), getHeight());
        offscreenGraphics = offscreenImage.getGraphics();
        long start;
        long end;

        // Tally the number of times we were able to draw direct and buffered
        int directCount = 0;
        int bufferedCount = 0;

        g.setColor(getBackground());
        // Mark what time we started
        start = System.currentTimeMillis();
        end = start;

        // Paint patterns directly to the screen, but only for 500 milliseconds
        while ((end-start) < 500) {
           paintDetectionDesign(g);
           end = System.currentTimeMillis();
           directCount++;
        }
        g.setColor(getForeground());

        // record the total time spent drawing directly
        long directTime = end - start;

        start = System.currentTimeMillis();
        end = start;

        // Paint patterns to the offscreen graphics, but only for 500 milliseconds
        while ((end-start) < 500) {
           paintDetectionDesign(offscreenGraphics);
           end = System.currentTimeMillis();
           bufferedCount++;
        }

        long bufferedTime = end - start;

        // If we were able to draw more times using the buffered graphics,
        // or if the drawing counts are the same, but the total time for
        // the buffering was less, buffering is faster.
        if ((bufferedCount > directCount) ||
           ((bufferedCount == directCount) &&
            (bufferedTime < directTime))) {
           drawDirect = false;
        } else {
        // If we want to draw direct, free the space taken up by the
        // offscreen image and graphics context.
           offscreenImage.flush();
           offscreenImage = null;
           offscreenGraphics = null;
           drawDirect = true;
        }
    }

    /** paintDetectionDesign performs some graphical operations to gauge the time
     * it takes to paint either directly or to an offscreen area. It just draws
     * some lines, boxes and ovals a number of times and then returns.
     */
     protected void paintDetectionDesign(Graphics g)
     {
          for (int i=0; i < 10; i++) {
               g.drawLine(0, 0, 100, 100);
               g.fillRect(0, 0, 100, 100);
               g.fillOval(0, 0, 100, 100);
          }
     }

     /** Display any reported errors to the user. Using a Message Dialog, the easily 
      * readable message is always shown and if and Exception is associated with 
      * the error, it is displayed in a scrollable text box below the message. */
    public void reportError(Error error) {
        Object[] dialogContent;        
        if(error.hasException()){
            javax.swing.JScrollPane exceptionPane = new javax.swing.JScrollPane();
            javax.swing.JTextArea exceptionTextArea = new javax.swing.JTextArea();
            exceptionTextArea.setText(error.getException().toString() + "\n");
            StackTraceElement[] stacktrace = error.getException().getStackTrace();
            for(int i = 0; i<stacktrace.length; i++){
                exceptionTextArea.append("   " + stacktrace[i].toString() + "\n");
            }
            exceptionPane.getViewport().setViewPosition(SelectableComponent.getDefaultOrigin());
            exceptionTextArea.setColumns(20);
            exceptionTextArea.setRows(5);
            exceptionPane.setViewportView(exceptionTextArea);
            dialogContent = new Object[]{error.getMessage() + "\n", exceptionPane};
        } else {
            dialogContent = new Object[]{error.getMessage() + "\n"};
        }
        
        JOptionPane.showMessageDialog(this, dialogContent, error.getTitle(), JOptionPane.ERROR_MESSAGE);
    }
    
    public Clipboard getClipboard() {
        return clipboard;
    }
    
    public ui.command.CommandHistory getCommandHistory() {
       return controller.getCommandHistory();
    }
    
    public javax.swing.JLabel getStatusAnimationLabel(){
        return statusAnimationLabel;
    }
    
    public javax.swing.JLabel getStatusMessageLabel(){
        return statusMessageLabel;
    }
    
    public javax.swing.JProgressBar getProgressBar(){
        return progressBar;
    }

    public void statusChange(ui.command.CommandStage stage, Object value) {
        controller.getCommandHistory().stageChange(stage, value);
    }
    
    // Options Panel Methods
    
    /** Reset the value of the Component Label Attribute */
    public void resetLabel(){
        if(sc!= null && sc.getProperties().getAttribute("Label") != null){
                sc.getProperties().getAttribute("Label").changeValue("");
        }
    }
    
    /** @return  the current value of the Component Label Attribute */
    public String getCurrentLabel(){
        if(sc==null){ return ""; } 
        return (String) sc.getProperties().getAttribute("Label").getValue();
    }
    
    /**
     * @return a copy of the component which the options panel displays.
     */
    public SelectableComponent getOptionsPanelComponent(){
        return (sc!=null)?sc.copy():null;        
    }
    
    /**Set the component which this panel shows. This component has already been
     * created and will normally come from a selection in the circuit workarea. */
    public void setOptionsPanelComponent(SelectableComponent sc){
        // Protect options panel from badly created components
        if(sc != null){
            this.sc = sc;
            titleLabel.setText((getActiveCircuit().getActiveComponents().contains(sc))?titleOld:titleNew);
            typeLabel.setText(sc.getName());
            if(!(sc instanceof ui.command.SubcircuitOpenCommand.SubcircuitComponent)){
                optionsPanel.setVisible(true);
                if(sc instanceof VisualComponent){
                    AttributesPanel.removeAll();
                    JPanel test = sc.getOptionsPanel();
                    AttributesPanel.add(test, BorderLayout.NORTH);
                    ((PreviewPanel)Preview).setComponent(sc);
                    AttributesPanel.repaint();
                    Preview.repaint();
                    AttributesPanel.setPreferredSize(test.getSize());
                    Toolbox.revalidate();
                }
            }            
            getActiveCircuit().repaint();
        } else {
            ErrorHandler.newError("Options Panel Error",
                    "The component that you are trying to create or select is malformed.");
        }
    }

    /** Rotate the component that is displayed in this options panel, also force
     * repaints as necessary. Changes to the component here are reflected in the 
     * unfixed component in the workarea. */
    public void setOptionsPanelComponentRotation(double d) {
        this.rotation = d;
        if(sc != null){
            sc.setRotation(rotation, false);
            ((PreviewPanel)Preview).setComponent(sc);
            Preview.repaint();
        }        
    }
    
    /** @return the current rotation of the component displayed in the panel. */
    public Double getOptionsPanelComponentRotation(){
        return rotation;
    }
    
    public void repaintOptionsPanel() {
        optionsPanel.repaint();
    }
    
    /** Fix a Component to the circuit
     * @param sc The component to fix */
    public void fixComponent(SelectableComponent sc) {
        controller.fixComponent(sc);
    }
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JMenuItem About;
    private javax.swing.JPanel AttributesPanel;
    private javax.swing.JButton ClearCircuit;
    javax.swing.JTree ComponentSelectionTree;
    private javax.swing.JMenuItem Copy;
    private javax.swing.JButton CopyButton;
    private javax.swing.JMenuItem Cut;
    private javax.swing.JButton CutButton;
    private javax.swing.JMenuItem Delete;
    private javax.swing.JButton DeleteSelection;
    javax.swing.JDesktopPane DesktopPane;
    private javax.swing.JMenu Edit;
    private javax.swing.JMenuItem Exit;
    private javax.swing.JMenu File;
    private javax.swing.JMenu Help;
    javax.swing.JButton InsertSubComponent;
    private javax.swing.JScrollPane MainScrollPane;
    private javax.swing.JButton MakeImageButton;
    private javax.swing.JMenuBar MenuBar;
    private javax.swing.JMenuItem New;
    private javax.swing.JButton NewButton;
    private javax.swing.JMenuItem Open;
    private javax.swing.JButton OpenFileButton;
    private javax.swing.JScrollPane OptionsPane;
    private javax.swing.JMenuItem Paste;
    private javax.swing.JButton PasteButton;
    private javax.swing.JMenuItem Preferences;
    private javax.swing.JPanel Preview;
    javax.swing.JMenuItem Record;
    javax.swing.JButton RecordButton;
    private javax.swing.JMenuItem Redo;
    private javax.swing.JButton RedoButton;
    javax.swing.JButton RotateLeft;
    javax.swing.JButton RotateRight;
    javax.swing.JMenuItem Run;
    private javax.swing.JMenuItem Save;
    private javax.swing.JMenuItem SaveAs;
    private javax.swing.JButton SaveAsButton;
    private javax.swing.JButton SaveButton;
    private javax.swing.JMenuItem SelectAll;
    javax.swing.JButton Selection;
    private javax.swing.JScrollPane SelectionTreeScrollPane;
    javax.swing.JMenu Simulation;
    javax.swing.JSlider SimulatorSpeed;
    javax.swing.JButton StartButton;
    javax.swing.JMenuItem StepForward;
    javax.swing.JButton StepForwardButton;
    javax.swing.JMenuItem Stop;
    javax.swing.JButton StopButton;
    private javax.swing.JButton ToggleGrid;
    private javax.swing.JToolBar Toolbar;
    private javax.swing.JPanel Toolbox;
    private javax.swing.JPanel ToolboxButtons;
    private javax.swing.JMenuItem Undo;
    private javax.swing.JButton UndoButton;
    private javax.swing.JMenu Window;
    javax.swing.JButton Wire;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JToolBar.Separator jSeparator3;
    private javax.swing.JToolBar.Separator jSeparator5;
    private javax.swing.JToolBar.Separator jSeparator6;
    javax.swing.JPanel optionsPanel;
    private javax.swing.JProgressBar progressBar;
    private javax.swing.JLabel statusAnimationLabel;
    private javax.swing.JLabel statusMessageLabel;
    private javax.swing.JPanel statusPanel;
    private javax.swing.JLabel titleLabel;
    private javax.swing.JLabel typeLabel;
    // End of variables declaration//GEN-END:variables
}